package nature.nano.component.part;

import java.util.Stack;

import nature.nano.component.SimpleProps;
import nature.nano.component.Strings;
import nature.nano.iface.Accepter;
import nature.nano.iface.enumer.EnumStringCallback;
import nature.nano.iface.secondary.Configable;
import nature.nano.iface.secondary.SlotBoard;

public abstract class AlphaNode implements Configable, SlotBoard {
	
	protected SimpleConfigable props = new SimpleConfigable();
	protected SimpleSlotBoard slots = new SimpleSlotBoard();

	private Stack<Object> outputWrittingTokens = new Stack<Object>();
	
	@Override
	public final int enumSlot(EnumStringCallback callback) {
		return slots.enumSlot(callback);
	}

	@Override
	public final int slotCount() {
		return slots.slotCount();
	}

	@Override
	public final Accepter getAccepter(String slot) {
		return slots.getAccepter(slot);
	}

	@Override
	public final Accepter plug(String slot, Accepter accepter) {
		return slots.plug(slot, accepter);
	}

	@Override
	public final int enumProp(EnumStringCallback callback) {
		return props.enumProp(callback);
	}

	@Override
	public final int propCount() {
		return props.propCount();
	}

	@Override
	public final String getProp(String key) {
		return props.getProp(key);
	}

	@Override
	public final String setProp(String key, String value) {
		String old = props.setProp(key, value);
		onSetProp(key, value);
		return old;
	}
	
	protected void slots(String... slots) {
		this.slots.initialize(slots);
	}

	protected Accepter slot(String slot) {
		return slots.slot(slot);
	}

	protected void props(String... keys) {
		props.initialize(keys);
	}
	
	protected String prop(String key) {
		return props.prop(key);
	}
	
	protected int intProp(String key, int defaultValue) {
		int value = defaultValue;
		String prop = prop(key);
		if (prop != null) {
			try {
				value = Integer.parseInt(prop);
			} catch (Exception e) {
			}
		}
		return value;		
	}
	
	private class WrittingToken {
		Stack<String> stack;
		Accepter accepter;
	}
	
	protected Object writeBegin(Accepter accepter, String... stuff) {
		if (stuff.length < 1 || stuff[0] == null) {
			return null;
		}
		
		Stack<String> stack = new Stack<String>();
		int current = 0;
		for (int i = 0; i < stuff.length; ++i) {
			int end = -1;
			if (stuff[i] == null) {
				end = i;
			} else if (i == stuff.length - 1) {
				end = i + 1;
			}
			
			if (end > current) {
				String type = stuff[current];
				current++;
				if ((end - current) % 2 != 0) {
					error(Strings.error, Strings.errorType, "invalid stuff", Strings.method, "write");
					return null;
				}
				SimpleProps props = new SimpleProps();
				for (int j = current; j < end; j += 2) {
					props.add(stuff[j], stuff[j + 1]);
				}
				accepter.onBegin(type, props);
				stack.add(type);
				current = end + 1;
			}
		}
		
		WrittingToken token = new WrittingToken();
		token.stack = stack;
		token.accepter = accepter;
		return token;		
	}
	
	protected Object writeBegin(String slot, String... stuff) {
		Accepter accepter = slot(slot);
		if (accepter == null) {
			error(Strings.error, Strings.errorType, "invalid slot", Strings.clazz, this.getClass().getSimpleName(), Strings.method, "write");
			return null;
		}
		return writeBegin(accepter, stuff);
	}
	
	protected void writeEnd(Object token) {
		if (!(token instanceof WrittingToken)) {
			return;
		}
		WrittingToken t = (WrittingToken) token;
		for (int i = t.stack.size() - 1; i >= 0; --i) {
			t.accepter.onEnd(t.stack.get(i));
		}
	}
	
	protected void write(String slot, String... stuff) {
		Object token = writeBegin(slot, stuff);
		writeEnd(token);
	}

	protected void write(Accepter accepter, String... stuff) {
		Object token = writeBegin(accepter, stuff);
		writeEnd(token);
	}
	
	protected void error(String... stuff) {
		write(Strings.error, stuff);
	}
	
	protected void outputBegin(String... stuff) {
		Object token = writeBegin(Strings.output, stuff);
		outputWrittingTokens.add(token);
	}
	
	protected void outputEnd() {
		writeEnd(outputWrittingTokens.pop());
	}
	
	protected void outputEndAll() {
		for (int i = outputWrittingTokens.size() - 1; i >= 0; --i) {
			writeEnd(outputWrittingTokens.get(i));
		}
		outputWrittingTokens.clear();
	}
	
	protected void output(String... stuff) {
		write(Strings.output, stuff);
	}

	protected Accepter output() {
		return slot(Strings.output);
	}

	protected Accepter error() {
		return slot(Strings.error);
	}
	
	protected abstract void onSetProp(String key, String value);
	
}
